<template>
  <div class="min-h-screen py-8 px-4 sm:px-6 lg:px-8">
    <div class="max-w-7xl mx-auto">
      <!-- 页面标题 -->
      <div class="text-center mb-12">
        <h1 class="text-4xl font-bold mb-4 tech-text">WebSocket API</h1>
        <p class="text-gray-300 text-lg">
          实时双向通信，适用于聊天应用、实时协作和游戏开发
        </p>
      </div>

      <!-- 浏览器支持提示 -->
      <div v-if="!isSupported" class="tech-card mb-8 border-yellow-600">
        <div class="flex items-center mb-4">
          <i class="i-carbon-warning text-yellow-500 text-xl mr-3"></i>
          <h3 class="text-xl font-semibold text-yellow-500">浏览器支持提示</h3>
        </div>
        <p class="text-gray-300">
          您的浏览器不支持 WebSocket API。此功能需要现代浏览器支持。
        </p>
      </div>

      <!-- WebSocket 连接 -->
      <div class="tech-card mb-8">
        <div class="flex items-center mb-6">
          <i class="i-carbon-network-3 text-3xl text-tech-accent mr-3"></i>
          <h2 class="text-2xl font-semibold text-white">WebSocket 连接</h2>
        </div>

        <div class="space-y-6">
          <!-- 服务器配置 -->
          <div class="bg-gray-800 rounded-lg p-6">
            <h3 class="text-white font-semibold mb-4">服务器配置</h3>
            
            <div class="space-y-4">
              <div>
                <label class="block text-gray-300 mb-2">WebSocket 服务器地址:</label>
                <input
                  v-model="serverUrl"
                  type="text"
                  placeholder="wss://echo.websocket.org"
                  :disabled="isConnected"
                  class="w-full bg-gray-700 text-white border border-gray-600 rounded-lg px-4 py-3 focus:border-tech-accent outline-none disabled:opacity-50"
                >
                <p class="text-gray-400 text-sm mt-1">
                  默认使用公共 Echo 服务器（会回显发送的消息）
                </p>
              </div>

              <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
                <button
                  @click="connect"
                  :disabled="isConnected"
                  class="tech-button disabled:opacity-50 disabled:cursor-not-allowed"
                >
                  <i class="i-carbon-connection-signal mr-2"></i>
                  {{ isConnected ? '已连接' : '连接' }}
                </button>

                <button
                  @click="disconnect"
                  :disabled="!isConnected"
                  class="bg-red-600 hover:bg-red-700 text-white px-6 py-3 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                >
                  <i class="i-carbon-close mr-2"></i>
                  断开
                </button>

                <button
                  @click="reconnect"
                  :disabled="isConnected || !hasConnectedBefore"
                  class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                >
                  <i class="i-carbon-renew mr-2"></i>
                  重连
                </button>

                <button
                  @click="clearMessages"
                  class="bg-gray-700 hover:bg-gray-600 text-white px-6 py-3 rounded-lg transition-colors"
                >
                  <i class="i-carbon-clean mr-2"></i>
                  清空
                </button>
              </div>
            </div>
          </div>

          <!-- 连接状态 -->
          <div class="bg-gray-800 rounded-lg p-6">
            <h3 class="text-white font-semibold mb-4">连接状态</h3>

            <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-gray-400 text-sm mb-1">连接状态</div>
                <div
                  :class="[
                    readyState === WebSocket.OPEN ? 'text-green-400' :
                    readyState === WebSocket.CONNECTING ? 'text-yellow-400' :
                    readyState === WebSocket.CLOSING ? 'text-orange-400' :
                    'text-red-400'
                  ]"
                  class="text-lg font-semibold"
                >
                  {{ getReadyStateText(readyState) }}
                </div>
              </div>

              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-gray-400 text-sm mb-1">协议</div>
                <div class="text-lg font-semibold text-white">
                  {{ protocol || 'N/A' }}
                </div>
              </div>

              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-gray-400 text-sm mb-1">扩展</div>
                <div class="text-lg font-semibold text-white">
                  {{ extensions || '无' }}
                </div>
              </div>
            </div>
          </div>

          <!-- 消息发送 -->
          <div v-if="isConnected" class="bg-gray-800 rounded-lg p-6">
            <h3 class="text-white font-semibold mb-4">发送消息</h3>

            <div class="space-y-4">
              <div class="flex gap-3">
                <input
                  v-model="message"
                  type="text"
                  placeholder="输入文本消息..."
                  @keyup.enter="sendTextMessage"
                  class="flex-1 bg-gray-700 text-white border border-gray-600 rounded-lg px-4 py-3 focus:border-tech-accent outline-none"
                >
                <button
                  @click="sendTextMessage"
                  :disabled="!message.trim()"
                  class="tech-button disabled:opacity-50 disabled:cursor-not-allowed"
                >
                  <i class="i-carbon-send mr-2"></i>
                  发送
                </button>
              </div>

              <div class="grid grid-cols-2 md:grid-cols-4 gap-3">
                <button
                  @click="sendJSON"
                  class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-lg transition-colors text-sm"
                >
                  <i class="i-carbon-code mr-1"></i>
                  发送 JSON
                </button>

                <button
                  @click="sendBinary"
                  class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors text-sm"
                >
                  <i class="i-carbon-data-blob mr-1"></i>
                  发送二进制
                </button>

                <button
                  @click="sendFile"
                  class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition-colors text-sm"
                >
                  <i class="i-carbon-document mr-1"></i>
                  发送文件
                </button>

                <button
                  @click="sendPing"
                  class="bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-2 rounded-lg transition-colors text-sm"
                >
                  <i class="i-carbon-radio-button mr-1"></i>
                  Ping 测试
                </button>
              </div>
            </div>
          </div>

          <!-- 消息历史 -->
          <div class="bg-gray-800 rounded-lg p-6">
            <div class="flex items-center justify-between mb-4">
              <h3 class="text-white font-semibold">消息历史</h3>
              <div class="flex items-center gap-3">
                <label class="flex items-center text-gray-300 text-sm">
                  <input
                    v-model="autoScroll"
                    type="checkbox"
                    class="mr-2"
                  >
                  自动滚动
                </label>
                <span class="text-gray-400 text-sm">{{ messages.length }} 条消息</span>
              </div>
            </div>

            <div
              ref="messageContainer"
              class="bg-gray-900 rounded-lg p-4 h-96 overflow-y-auto font-mono text-sm"
            >
              <div v-if="messages.length === 0" class="text-gray-400 text-center py-8">
                暂无消息
              </div>
              <div
                v-for="(msg, index) in messages"
                :key="index"
                class="mb-3 pb-3 border-b border-gray-800 last:border-0"
              >
                <div class="flex items-start justify-between mb-1">
                  <span
                    :class="[
                      msg.type === 'sent' ? 'text-blue-400' :
                      msg.type === 'received' ? 'text-green-400' :
                      msg.type === 'error' ? 'text-red-400' :
                      'text-yellow-400'
                    ]"
                    class="font-semibold"
                  >
                    {{ getMessageTypeIcon(msg.type) }} {{ getMessageTypeText(msg.type) }}
                  </span>
                  <span class="text-gray-500 text-xs">{{ msg.timestamp }}</span>
                </div>
                <div class="text-gray-300 break-words">{{ msg.content }}</div>
                <div v-if="msg.size" class="text-gray-500 text-xs mt-1">
                  大小: {{ formatBytes(msg.size) }}
                </div>
              </div>
            </div>
          </div>

          <!-- 统计信息 -->
          <div class="bg-gray-800 rounded-lg p-6">
            <h3 class="text-white font-semibold mb-4">统计信息</h3>

            <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-3xl font-bold text-blue-400">{{ stats.messagesSent }}</div>
                <div class="text-gray-400 text-sm mt-1">已发送</div>
              </div>

              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-3xl font-bold text-green-400">{{ stats.messagesReceived }}</div>
                <div class="text-gray-400 text-sm mt-1">已接收</div>
              </div>

              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-3xl font-bold text-purple-400">{{ formatBytes(stats.bytesSent) }}</div>
                <div class="text-gray-400 text-sm mt-1">发送字节</div>
              </div>

              <div class="bg-gray-900 rounded-lg p-4 text-center">
                <div class="text-3xl font-bold text-yellow-400">{{ formatBytes(stats.bytesReceived) }}</div>
                <div class="text-gray-400 text-sm mt-1">接收字节</div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 快速测试服务器 -->
      <div class="tech-card mb-8">
        <h2 class="text-2xl font-semibold mb-6 text-white">快速测试服务器</h2>

        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
          <button
            @click="connectToServer('wss://echo.websocket.org')"
            :disabled="isConnected"
            class="bg-gray-800 hover:bg-gray-700 text-white p-6 rounded-lg transition-colors text-left disabled:opacity-50 disabled:cursor-not-allowed"
          >
            <i class="i-carbon-cloud text-3xl text-blue-400 mb-3 block"></i>
            <div class="text-lg font-semibold mb-2">Echo Server</div>
            <div class="text-gray-400 text-sm">回显测试服务器</div>
            <code class="text-xs text-gray-500 mt-2 block">wss://echo.websocket.org</code>
          </button>

          <button
            @click="connectToServer('wss://ws.postman-echo.com/raw')"
            :disabled="isConnected"
            class="bg-gray-800 hover:bg-gray-700 text-white p-6 rounded-lg transition-colors text-left disabled:opacity-50 disabled:cursor-not-allowed"
          >
            <i class="i-carbon-network-3 text-3xl text-green-400 mb-3 block"></i>
            <div class="text-lg font-semibold mb-2">Postman Echo</div>
            <div class="text-gray-400 text-sm">Postman 测试服务</div>
            <code class="text-xs text-gray-500 mt-2 block">wss://ws.postman-echo.com/raw</code>
          </button>

          <button
            @click="connectToServer('ws://localhost:8080')"
            :disabled="isConnected"
            class="bg-gray-800 hover:bg-gray-700 text-white p-6 rounded-lg transition-colors text-left disabled:opacity-50 disabled:cursor-not-allowed"
          >
            <i class="i-carbon-server text-3xl text-purple-400 mb-3 block"></i>
            <div class="text-lg font-semibold mb-2">本地服务器</div>
            <div class="text-gray-400 text-sm">本地开发测试</div>
            <code class="text-xs text-gray-500 mt-2 block">ws://localhost:8080</code>
          </button>
        </div>
      </div>

      <!-- API 信息 -->
      <div class="tech-card">
        <h3 class="text-xl font-semibold mb-4 text-white">API 信息</h3>
        <div class="space-y-4 text-gray-300">
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">浏览器支持</h4>
            <p>所有现代浏览器都支持 WebSocket API</p>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">核心 API</h4>
            <ul class="list-disc list-inside space-y-1">
              <li><code class="bg-gray-800 px-2 py-1 rounded">new WebSocket(url, protocols?)</code> - 创建连接</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">websocket.send(data)</code> - 发送数据</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">websocket.close(code?, reason?)</code> - 关闭连接</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">websocket.readyState</code> - 连接状态</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">websocket.bufferedAmount</code> - 缓冲数据量</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">事件处理</h4>
            <ul class="list-disc list-inside space-y-1">
              <li><code class="bg-gray-800 px-2 py-1 rounded">onopen</code> - 连接建立时触发</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">onmessage</code> - 接收到消息时触发</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">onerror</code> - 发生错误时触发</li>
              <li><code class="bg-gray-800 px-2 py-1 rounded">onclose</code> - 连接关闭时触发</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">连接状态</h4>
            <ul class="list-disc list-inside space-y-1 text-sm">
              <li><strong>CONNECTING (0):</strong> 正在连接</li>
              <li><strong>OPEN (1):</strong> 连接已建立，可以通信</li>
              <li><strong>CLOSING (2):</strong> 连接正在关闭</li>
              <li><strong>CLOSED (3):</strong> 连接已关闭</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">基本用法</h4>
            <pre class="bg-gray-800 p-4 rounded text-sm overflow-x-auto">
<span class="text-gray-500">// 创建 WebSocket 连接</span>
<span class="text-blue-400">const</span> ws = <span class="text-blue-400">new</span> WebSocket(<span class="text-green-400">'wss://echo.websocket.org'</span>);

<span class="text-gray-500">// 连接建立</span>
ws.onopen = () => {
  console.log(<span class="text-green-400">'连接已建立'</span>);
  ws.send(<span class="text-green-400">'Hello Server!'</span>);
};

<span class="text-gray-500">// 接收消息</span>
ws.onmessage = (event) => {
  console.log(<span class="text-green-400">'收到消息:'</span>, event.data);
};

<span class="text-gray-500">// 连接关闭</span>
ws.onclose = () => {
  console.log(<span class="text-green-400">'连接已关闭'</span>);
};

<span class="text-gray-500">// 错误处理</span>
ws.onerror = (error) => {
  console.error(<span class="text-green-400">'WebSocket 错误:'</span>, error);
};</pre>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">数据类型支持</h4>
            <ul class="list-disc list-inside space-y-1 text-sm">
              <li><strong>String:</strong> 文本消息（UTF-8 编码）</li>
              <li><strong>Blob:</strong> 二进制大对象</li>
              <li><strong>ArrayBuffer:</strong> 原始二进制数据</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">使用场景</h4>
            <ul class="list-disc list-inside space-y-1 text-sm">
              <li>实时聊天和即时通讯</li>
              <li>在线游戏和多人协作</li>
              <li>实时数据推送和监控</li>
              <li>股票行情和金融数据</li>
              <li>实时通知和提醒</li>
              <li>协同编辑和白板应用</li>
              <li>IoT 设备实时控制</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">最佳实践</h4>
            <ul class="list-disc list-inside space-y-1 text-sm">
              <li>实现自动重连机制</li>
              <li>使用心跳保持连接活跃</li>
              <li>处理网络异常和超时</li>
              <li>使用 WSS (WebSocket Secure) 加密传输</li>
              <li>限制消息大小和发送频率</li>
              <li>妥善处理 bufferedAmount</li>
              <li>在页面卸载时关闭连接</li>
            </ul>
          </div>
          <div>
            <h4 class="font-semibold text-tech-accent mb-2">与 HTTP 对比</h4>
            <div class="text-sm space-y-2">
              <p><strong>WebSocket 优势：</strong></p>
              <ul class="list-disc list-inside ml-4">
                <li>全双工通信，服务器可主动推送</li>
                <li>更低的延迟和开销</li>
                <li>保持长连接，减少握手次数</li>
              </ul>
              <p class="mt-2"><strong>HTTP 优势：</strong></p>
              <ul class="list-disc list-inside ml-4">
                <li>更好的缓存支持</li>
                <li>更成熟的生态和工具</li>
                <li>适合请求-响应模式</li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onUnmounted, nextTick, watch } from 'vue'

// 类型定义
interface Message {
  content: string
  type: 'sent' | 'received' | 'system' | 'error'
  timestamp: string
  size?: number
}

interface Stats {
  messagesSent: number
  messagesReceived: number
  bytesSent: number
  bytesReceived: number
}

// 响应式状态
const isSupported = ref(typeof WebSocket !== 'undefined')
const serverUrl = ref('wss://echo.websocket.org')
const message = ref('')
const messages = ref<Message[]>([])
const isConnected = ref(false)
const hasConnectedBefore = ref(false)
const readyState = ref(WebSocket.CLOSED)
const protocol = ref('')
const extensions = ref('')
const autoScroll = ref(true)
const messageContainer = ref<HTMLElement | null>(null)

let websocket: WebSocket | null = null

// 统计
const stats = ref<Stats>({
  messagesSent: 0,
  messagesReceived: 0,
  bytesSent: 0,
  bytesReceived: 0
})

// 监听消息变化，自动滚动
watch(() => messages.value.length, () => {
  if (autoScroll.value) {
    nextTick(() => {
      if (messageContainer.value) {
        messageContainer.value.scrollTop = messageContainer.value.scrollHeight
      }
    })
  }
})

// 清理
onUnmounted(() => {
  disconnect()
})

// 连接
const connect = () => {
  if (isConnected.value) return

  try {
    websocket = new WebSocket(serverUrl.value)
    
    websocket.onopen = () => {
      isConnected.value = true
      hasConnectedBefore.value = true
      readyState.value = websocket?.readyState || WebSocket.CLOSED
      protocol.value = websocket?.protocol || ''
      extensions.value = websocket?.extensions || ''
      addMessage('WebSocket 连接已建立', 'system')
    }
    
    websocket.onmessage = (event) => {
      const size = typeof event.data === 'string' 
        ? new Blob([event.data]).size 
        : event.data.size || event.data.byteLength || 0
      
      addMessage(event.data, 'received', size)
      stats.value.messagesReceived++
      stats.value.bytesReceived += size
    }
    
    websocket.onclose = (event) => {
      isConnected.value = false
      readyState.value = WebSocket.CLOSED
      addMessage(`连接已关闭 (代码: ${event.code}, 原因: ${event.reason || '无'})`, 'system')
    }
    
    websocket.onerror = () => {
      addMessage('WebSocket 连接发生错误', 'error')
    }

    // 更新状态
    const updateState = setInterval(() => {
      if (websocket) {
        readyState.value = websocket.readyState
      } else {
        clearInterval(updateState)
      }
    }, 100)
  } catch (error) {
    addMessage(`连接失败: ${error}`, 'error')
  }
}

// 断开连接
const disconnect = () => {
  if (websocket) {
    websocket.close(1000, '用户主动断开')
    websocket = null
  }
}

// 重新连接
const reconnect = () => {
  disconnect()
  setTimeout(() => connect(), 500)
}

// 连接到指定服务器
const connectToServer = (url: string) => {
  serverUrl.value = url
  connect()
}

// 发送文本消息
const sendTextMessage = () => {
  if (!websocket || !message.value.trim()) return

  try {
    websocket.send(message.value)
    const size = new Blob([message.value]).size
    addMessage(message.value, 'sent', size)
    stats.value.messagesSent++
    stats.value.bytesSent += size
    message.value = ''
  } catch (error) {
    addMessage(`发送失败: ${error}`, 'error')
  }
}

// 发送 JSON
const sendJSON = () => {
  if (!websocket) return

  const data = {
    type: 'test',
    message: 'Hello from WebSocket API Demo',
    timestamp: Date.now(),
    random: Math.random()
  }

  try {
    const jsonStr = JSON.stringify(data, null, 2)
    websocket.send(jsonStr)
    const size = new Blob([jsonStr]).size
    addMessage(`发送 JSON: ${jsonStr}`, 'sent', size)
    stats.value.messagesSent++
    stats.value.bytesSent += size
  } catch (error) {
    addMessage(`发送 JSON 失败: ${error}`, 'error')
  }
}

// 发送二进制数据
const sendBinary = () => {
  if (!websocket) return

  const data = new Uint8Array([72, 101, 108, 108, 111]) // "Hello"
  
  try {
    websocket.send(data.buffer)
    addMessage(`发送二进制数据: [${Array.from(data).join(', ')}]`, 'sent', data.byteLength)
    stats.value.messagesSent++
    stats.value.bytesSent += data.byteLength
  } catch (error) {
    addMessage(`发送二进制数据失败: ${error}`, 'error')
  }
}

// 发送文件
const sendFile = () => {
  if (!websocket) return

  const input = document.createElement('input')
  input.type = 'file'
  
  input.onchange = (e) => {
    const file = (e.target as HTMLInputElement).files?.[0]
    if (file) {
      const reader = new FileReader()
      reader.onload = () => {
        try {
          websocket?.send(reader.result as ArrayBuffer)
          addMessage(`发送文件: ${file.name} (${file.size} bytes)`, 'sent', file.size)
          stats.value.messagesSent++
          stats.value.bytesSent += file.size
        } catch (error) {
          addMessage(`发送文件失败: ${error}`, 'error')
        }
      }
      reader.readAsArrayBuffer(file)
    }
  }
  
  input.click()
}

// Ping 测试
const sendPing = () => {
  if (!websocket) return

  const pingTime = Date.now()
  const pingMsg = `PING ${pingTime}`
  
  try {
    websocket.send(pingMsg)
    addMessage(pingMsg, 'sent')
    stats.value.messagesSent++
  } catch (error) {
    addMessage(`Ping 失败: ${error}`, 'error')
  }
}

// 清空消息
const clearMessages = () => {
  messages.value = []
  stats.value = {
    messagesSent: 0,
    messagesReceived: 0,
    bytesSent: 0,
    bytesReceived: 0
  }
}

// 添加消息
const addMessage = (content: string, type: Message['type'], size?: number) => {
  messages.value.push({
    content,
    type,
    timestamp: new Date().toLocaleTimeString('zh-CN'),
    size
  })
}

// 获取状态文本
const getReadyStateText = (state: number): string => {
  const states: Record<number, string> = {
    [WebSocket.CONNECTING]: '连接中',
    [WebSocket.OPEN]: '已连接',
    [WebSocket.CLOSING]: '关闭中',
    [WebSocket.CLOSED]: '已关闭'
  }
  return states[state] || '未知'
}

// 获取消息类型文本
const getMessageTypeText = (type: string): string => {
  const types: Record<string, string> = {
    sent: '发送',
    received: '接收',
    system: '系统',
    error: '错误'
  }
  return types[type] || type
}

// 获取消息类型图标
const getMessageTypeIcon = (type: string): string => {
  const icons: Record<string, string> = {
    sent: '↑',
    received: '↓',
    system: 'ℹ',
    error: '✗'
  }
  return icons[type] || '•'
}

// 格式化字节
const formatBytes = (bytes: number): string => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
</script>

<style scoped>
/* 滚动条样式 */
::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: #1f2937;
}

::-webkit-scrollbar-thumb {
  background: #4b5563;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #6b7280;
}
</style>
